//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Utils/Texture/Image/PixelFormat.h"
#include <stl_ext/Math/ColorFormats.h>
#include <stl_ext/Math/ImageUtils.h>

namespace Engine
{
namespace Utils
{

#	define PIXEL_FORMAT_TO_COLOR_FORMAT( _decl ) \
		/* unsigned normalized */ \
		_decl( ALPHA8,		R8_UNorm ) \
		_decl( R5_G6_B5,	R5_G6_B5_UNorm ) \
		_decl( RGB8,		RGB8_UNorm ) \
		_decl( RGB5_A1,		RGB5_A1_UNorm ) \
		_decl( RGBA4,		RGBA4_UNorm ) \
		_decl( RGBA8,		RGBA8_UNorm ) \
		_decl( R8,			R8_UNorm ) \
		_decl( RG8,			RG8_UNorm ) \
		/* float (EXT_texture_storage) */ \
		_decl( RGBA32F,		RGBA32f ) \
		_decl( RGBA16F,		RGBA16f ) \
		_decl( RGB32F,		RGB32f ) \
		_decl( RGB16F,		RGB16f ) \
		_decl( RG32F,		RG32f ) \
		_decl( RG16F,		RG16f ) \
		_decl( R32F,		R32f ) \
		_decl( R16F,		R16f ) \
		_decl( ALPHA32F,	R32f ) \
		_decl( ALPHA16F,	R16f ) \
		/* float (EXT_texture_storage) normalized */ \
		_decl( RGBA32F_NORM,	RGBA32f_Norm ) \
		_decl( RGBA16F_NORM,	RGBA16f_Norm ) \
		_decl( RGB32F_NORM,		RGB32f_Norm ) \
		_decl( RGB16F_NORM,		RGB16f_Norm ) \
		_decl( RG32F_NORM,		RG32f_Norm ) \
		_decl( RG16F_NORM,		RG16f_Norm ) \
		_decl( R32F_NORM,		R32f_Norm ) \
		_decl( R16F_NORM,		R16f_Norm ) \
		_decl( ALPHA32F_NORM,	R32f_Norm ) \
		_decl( ALPHA16F_NORM,	R16f_Norm ) \
		/* depth * \
		_decl( D16 ) \
		_decl( D24 ) \
		_decl( D32 ) \
		/* stencil * \
		_decl( S1 ) \
		_decl( S4 ) \
		_decl( S8 ) \
		/* depth stencil * \
		_decl( D24_S8 ) \
		/* compressed * \
		_decl( RGB_ETC1 ) \
		*/




	//
	// Pixel Format To Color Format
	//
	
	template <EPixelFormat::type PF>
	struct PixelFormatToColorFormatStatic
	{};

#	define BUILD_PF_TO_CF_STATIC( _pf, _cf ) \
		template <> \
		struct PixelFormatToColorFormatStatic < EPixelFormat::_pf > \
		{ \
			typedef UXMathExt::ColorFormat::_cf	type; \
		};

	PIXEL_FORMAT_TO_COLOR_FORMAT( BUILD_PF_TO_CF_STATIC )

#	undef BUILD_PF_TO_CF_STATIC


	typedef bool (* ConvertImageDataFunc_t) (const uvec3 &srcDim, const uvec3 &srcOff, const void *src,
											 const uvec3 &dstDim, const uvec3 &dstOff, void *dst,
											 uint srcAlignInBytes, uint dstAlignInBytes);
	
	typedef bool (* FillImageFunc_t) (const uvec3 &srcDim, const void *srcColor,
									  const uvec3 &dstOff, const uvec3 &dstDim, void *dst,
									  uint dstAlignInBytes);


	//
	// Pixel Format To Color Format
	//

	struct PixelFormatToColorFormat
	{
	public:
		// GetImageSize
		static usize GetImageSize (const uvec3 &dimension, uint rowAlignInBytes, EPixelFormat::type format)
		{
			const formats_map_t &	m = _GetConverters();

			usize	index = -1;

			if ( m.FindIndex( format, index ) )
			{
				const uvec3	dim = Max( dimension, uvec3(1) );

				const usize	row_size = UXMathExt::AlignedImageRowSize( dim.x, Bytes(m[ index ].second.sizeOf).ToBits(), Bytes(rowAlignInBytes) );

				return row_size * dim.y * dim.z;
			}

			WARNING( "!!!" );
			return 0;
		}

		
		// GetConverterProc
		static ConvertImageDataFunc_t GetConverterProc (EPixelFormat::type input, EPixelFormat::type output)
		{
			ImageFuncs const *	funcs = _FindFuncs( input, output );
			return funcs != null ? funcs->conv : null;
		}

		
		// GetFillProc
		static FillImageFunc_t GetFillProc (EPixelFormat::type input, EPixelFormat::type output)
		{
			ImageFuncs const *	funcs = _FindFuncs( input, output );
			return funcs != null ? funcs->fill : null;
		}


	private:

		//
		// ImageFuncs
		//
		struct ImageFuncs
		{
			ConvertImageDataFunc_t	conv;
			FillImageFunc_t			fill;
			///
			ImageFuncs () : conv(null), fill(null) {}
			ImageFuncs (ConvertImageDataFunc_t conv, FillImageFunc_t fill) : conv(conv), fill(fill) {}
		};

		typedef map< EPixelFormat::type, ImageFuncs >	convproc_map_t;


		//
		// FormatInfo
		//
		struct FormatInfo
		{
			convproc_map_t	convprocs;
			uint			sizeOf;
			///
			FormatInfo () : sizeOf(0) {}
			FormatInfo (const convproc_map_t &procs, uint sizeOf) : convprocs(procs), sizeOf(sizeOf) {}
		};

		typedef map< EPixelFormat::type, FormatInfo >			formats_map_t;

		
		// _GetProc1Map
		template < EPixelFormat::type InputPF, typename SrcType
				 >
		static convproc_map_t _GetProc1Map ()
		{
#			define BUILD_PF_TO_CF_2( _pf, _cf ) \
				m.Add( EPixelFormat::_pf, ImageFuncs( \
						&_GetProc1< InputPF, SrcType, EPixelFormat::_pf, UXMathExt::ColorFormat::_cf >, \
						&_GetProc2< InputPF, SrcType, EPixelFormat::_pf, UXMathExt::ColorFormat::_cf > ) );

			convproc_map_t	m;
			PIXEL_FORMAT_TO_COLOR_FORMAT( BUILD_PF_TO_CF_2 );
			return m;

#			undef BUILD_PF_TO_CF_2
		}


		// _GetProcMap
		static formats_map_t _GetProcMap ()
		{
#			define BUILD_PF_TO_CF_1( _pf, _cf ) \
				m.Add( EPixelFormat::_pf, FormatInfo( \
					_GetProc1Map< EPixelFormat::_pf, UXMathExt::ColorFormat::_cf >(), sizeof(UXMathExt::ColorFormat::_cf) ) );

			formats_map_t	m;
			PIXEL_FORMAT_TO_COLOR_FORMAT( BUILD_PF_TO_CF_1 );
			return m;

#			undef BUILD_PF_TO_CF_1
		}


		// _GetConverters
		static const formats_map_t &  _GetConverters ()
		{
			static const formats_map_t	procs = _GetProcMap();
			return procs;
		}
		

		// _FindFuncs
		static ImageFuncs const *  _FindFuncs (EPixelFormat::type input, EPixelFormat::type output)
		{
			if ( input == EPixelFormat::_UNKNOWN or output == EPixelFormat::_UNKNOWN )
				return null;

			const formats_map_t &	m = _GetConverters();

			usize	index1 = -1;

			if ( m.FindIndex( input, index1 ) )
			{
				usize	index2 = -1;

				if ( m[ index1 ].second.convprocs.FindIndex( output, index2 ) )
				{
					return &m[ index1 ].second.convprocs[ index2 ].second;
				}
			}

			WARNING( "!!!" );
			return null;
		}


		// _GetProc1
		template < EPixelFormat::type InputPF,  typename SrcType,
				   EPixelFormat::type OutputPF, typename DstType
				 >
		static bool _GetProc1 (const uvec3 &srcDim, const uvec3 &srcOff, const void *src,
							   const uvec3 &dstDim, const uvec3 &dstOff, void *dst,
							   uint srcAlignInBytes, uint dstAlignInBytes)
		{
			return UXMathExt::ImageUtils::CopyPart(
						srcDim, srcOff, (const SrcType *) src,
						dstDim, dstOff, (DstType *) dst,
						Bytes(srcAlignInBytes), Bytes(dstAlignInBytes) );
		}


		// _GetProc2
		template < EPixelFormat::type InputPF,  typename SrcType,
				   EPixelFormat::type OutputPF, typename DstType
				 >
		static bool _GetProc2 (const uvec3 &srcDim, const void *srcColor,
								const uvec3 &dstOff, const uvec3 &dstDim, void *dst,
								uint dstAlignInBytes)
		{
			return UXMathExt::ImageUtils::Fill(
						srcDim, *(const SrcType *) srcColor,
						dstOff, dstDim, (DstType *) dst, Bytes(dstAlignInBytes) );
		}
	};
	
	
#	undef PIXEL_FORMAT_TO_COLOR_FORMAT


}	// Utils
}	// Engine
